spring这个庞大的家族给了我们很大便利的同时,也给了我们很多的眼界。使得我们可以模仿它,有个能模仿它的框架,有的能模仿它的某个知识点。现在,我们来对spring, spring boot, spring cloud中的涉及加载.class文件方式进行一次摸底,形成一个草集。
当前,在spring框架中,主要有以下种类的文件.class文件,spring.factories文件,.properties/.yml文件,.xml文件等等。前三种距离实际开发工作更近,所有我们详说前三种,下面我们就看看这三种resource资源文件spring是如何加载解析的
加载.class文件 虽然都是加载.class文件,也都是从classpath*下去寻找并加载,但是在入参上提供了不同层次的传入,方式一提供的是注解属性集合和指定类class,方式二是spring cloud openfeign自己的层级参入。这两种方式都可以为我们直接使用。当然,最终用的还是公共(通用)部分,即:ClassPathScanningCandidateComponentProvider.findCandidateComponents(String)
方式一 解析注解属性值确定package目录从而实现加载classpath下指定目录 .class文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ComponentScanAnnotationParser class public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass ) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); scanner.setBeanNameGenerator(this.beanNameGenerator); scanner.setScopedProxyMode(scopedProxyMode); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); scanner.setResourcePattern(componentScan.getString("resourcePattern")); scanner.addIncludeFilter(typeFilter); scanner.addExcludeFilter(typeFilter); scanner.getBeanDefinitionDefaults().setLazyInit(true); Set<String> basePackages = new LinkedHashSet<>(); for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } return scanner.doScan(StringUtils.toStringArray(basePackages)); } protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 调用下面的公共部分的方法,即ClassPathScanningCandidateComponentProvider.findCandidateComponents(String) Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
方式二 加载指定package目录下的.class文件 spring cloud openfeign
作为RPC调用组件,他没有直接使用spring
通用的逻辑,而是自己在加载feign组件的.class
时,它自己实现了一套加载逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 FeignClientsRegistrar class public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); Set<String> basePackages = getBasePackages(metadata); for (String basePackage : basePackages) { // 调用下面的公共部分的方法,即ClassPathScanningCandidateComponentProvider.findCandidateComponents(String) Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes(FeignClient.class.getCanonicalName()); registerFeignClient(registry, annotationMetadata, attributes); } } } }
通过创建ClassPathScanningCandidateComponentProvider
对象,并给他赋值类加载器、目标类型等。同时提供package目录就可以完成加载其下的.class文件,然后生成BeanDefinition放入spring beanFactory容器。这可以说就是一个工具类
公共部分(即实现原理内部)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ClassPathScanningCandidateComponentProvider class public Set<BeanDefinition> findCandidateComponents(String basePackage) { return scanCandidateComponents(basePackage); } private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); // 如 classpath*:com/skyler/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); candidates.add(sbd); } } return candidates; }
如方法所示,加载的都是classpath*下指定目录的.class们
加载.factories文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 List<EnvironmentPostProcessor> loadPostProcessors() { // 核心方法 return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader()); } // 应用示例 private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }
在项目下所有jar包中的META-INF/spring.factories加载指定的class类型对象集合。如上方法,加载EnvironmentPostProcessor类型的对象集合
加载.properties/.yml文件 详见:ConfigFileApplicationListener class
扩展 AnnotatedBeanDefinitionReader与ClassPathBeanDefinitionScanner各自功能及区别1 2 3 4 public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
java后端开发,to B业务,趋于蓝色的性格,喜欢篮球等